from khronos.utils.misc import Namespace
from khronos.utils.call import Call 

class Config(object):
    """Configuration manager object. Should be used as a class attribute, and will also work as a 
    descriptor on the class instances, providing a very convenient way to validate the instance's 
    configuration.
        Example:
            class foo(object):
                config = Config(required=["x", "y"],
                                optional={"z": 0})
                def __init__(self, **params):
                    self.__dict__.update(params)
                    
            f = foo(x=1)
            f.config() # or foo.config(f)
            # will raise ValueError: missing configuration parameters ['y']
            print f.__dict__
            # will print {'x': 1, 'z': 0}
    """
    def __init__(self, required=(), optional=(), inherit=True):
        self.required = set(required)
        self.optional = Namespace(optional)
        self.inherit = bool(inherit)
        
    def __get__(self, obj, owner):
        if obj is not None:
            return Call(self.__call__, obj)
        return self
        
    def __call__(self, obj, validate=True, complete=True, inherit=None):
        if inherit is None:
            inherit = self.inherit
        cls_list = reversed(obj.__class__.mro()) if inherit else [obj.__class__]
        if validate:
            required = set()
        for cls in cls_list:
            if "config" not in cls.__dict__ or not isinstance(cls.config, Config):
                continue
            if validate:
                required.update(cls.config.required)
            if complete:
                for param, value in cls.config.optional.iteritems():
                    if param not in obj.__dict__:
                        obj.__dict__[param] = value
        if validate:
            required.difference_update(obj.__dict__.iterkeys())
            if len(required) > 0:
                raise ValueError("missing configuration parameters %s" % (list(required),))
                